<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>组合式继承解析</title>
  </head>
  <body>
    <h3>组合式继承解析</h3>
    <h4>由于要打印原型对象，在浏览器操作比较方便，就用了html文件。</h4>
    <script>
      /*
       * @Author: Penk是个码农
       * @Date: 2023-07-24 10:40:46
       * @Last Modified by: Penk是个码农
       * @Last Modified time: 2023-07-24 11:37:02
       * @Desc: 组合式继承解析
       */

      /**
       * 父类：Parent，要被继承的类
       * 子类：Children，要继承父类的类
       * 实例：person1 ,person2，实例化的子类，一般实例化后数据就分开了，复合数据类型要注意...
       *
       * 构造函数：构造函数也称之为构造器（constructor），通常是我们在创建对象时会调用的函数。
       *          在其他面向的编程语言里面，构造函数是存在于类中的一个方法。
       *          虽然es6 中的class也是解决了旧的JS继承方式，但是无奈考官要提问，只能记住了 ：) ...
       * 原型：原型就是一个对象，也叫原型对象，不同的人叫法不一样，很容易搞懵逼初学者，原型===原型对象
       * 原型链：就是实例对象和原型对象之间的链接，每一个对象都有原型，原型本身又是对象，原型又有原型，以此类推形成一个链式结构，称为原型链。
       * prototype：子类构造函数 所指向的原型（实例化的父类）。
       *            Children.prototype = new Parent(); 这个是手动指向
       * __proto__：子类实例对象 所指向的原型（实例化的父类）。 这个是new 操作符里面的操作
       */

      // 父类
      function Parent(name, age, dataA, dataB) {
        console.warn("Parent 实例化了...");
        // 基本数据类型
        this.name = name;
        this.age = age;
        // 复合数据类型
        this.data = { a: dataA ? dataA : "a", b: dataB ? dataB : "b", c: "c" };

        // 父类内部方法
        this.sayName = function () {
          console.log("我的名字：", this.name);
        };

        // 全局的关键是看这个...原型链的bug在这里
        this.sayData = function () {
          // 这里还不能直接打印对象出来，因为是一个指针，总是指向最新的数据...
          console.log("我的数据如下：");
          console.dir(this.data.a);
          console.dir(this.data.b);
          console.dir(this.data.c);
          console.log("我的数据结束====");
        };
      }

      // 父类原型链方法 (这个跟原型链继承只有半毛钱关系...)
      Parent.prototype.sayAge = function () {
        console.log("我的年龄：", this.age);
      };

      // 子类，不同工种
      function Children(name, age, dataA, dataB, job) {
        // 原型链继承无法通过super 传递给父类，只能通过覆盖的形式了
        Parent.call(this, name, age, dataA, dataB);
        this.job = job;
        this.sayJob = function () {
          console.log("我的工作：", this.job);
        };
      }

      // 原型链继承，构造函数的原型，是一个对象，才会在内存在分配空间，所以是需要实例化的。
      // 这边只写了一个子类，如果是多个子类，其实就会实例化多个原型对象，一般数据不会相互影响，除了复合数据类型，这边不进行验证...
      Children.prototype = new Parent();

      console.log("===person1...");
      let person1 = new Children(
        "钟先生",
        33,
        "person1A",
        "person1B",
        "程序员"
      );
      // 基本数据类型
      person1.sayName();
      person1.sayAge();
      person1.sayJob();
      // 复合数据类型
      person1.sayData();

      console.log("===person2...");
      let person2 = new Children(
        "刘小姐",
        18,
        "person2A",
        "person2B",
        "清洁阿姨"
      );
      // 基本数据类型
      person2.sayName();
      person2.sayAge();
      person2.sayJob();
      // 复合数据类型
      person2.sayData();

      console.log("===重新打印person1，不用实例化，看看person1会不会被影响...");
      // 基本数据类型
      person1.sayName();
      person1.sayAge();
      person1.sayJob();
      // 复合数据类型
      person1.sayData();
      console.log(
        "===可以看出，基本数据类型不会被影响到，但是复合数据类型出事了..."
      );

      console.dir(person1);
    </script>
  </body>
</html>
